home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Games Collection 1 / software vault.zip / software vault / CDR10 / ACK3D.ZIP / ENGINE / XMSLIB.C < prev    next >
Text File  |  1993-07-19  |  26KB  |  978 lines

  1. /* X M S L I B . c -- Extended memory (XMS 2.0+) support functions
  2.  * ---------------------------------------------------------------
  3.  *
  4.  * $Revision:   1.0  $
  5.  *     $Date:   28 Feb 1992  8:54:56  $
  6.  *      $Log:   F:/XMSLIB/VCS/XMSLIB.C_V  $
  7.  * 
  8.  *    Rev 1.0   28 Feb 1992  8:54:56
  9.  * Initial revision.
  10.  * 
  11.  * ---------------------------------------------------------------
  12.  */
  13.  
  14. /* ------------------------  Pragmas -------------------------- */
  15.  
  16. /* ---------------------- Include files ----------------------- */
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #if defined ( _M_DEBUG )
  20.   #include <ctype.h>
  21. #endif
  22. #include <dos.h>
  23.  
  24. #include "xmslib.h"
  25.  
  26. /* -------------------- Local definitions --------------------- */
  27. #define _CPYR_ " Copyright (C) 1992 M.G.A.Wilson"
  28.  
  29. #if defined ( M_I86SM )                      // Microsoft, small
  30.   #define APICALL   call far ptr [mpXMSAPI]
  31.   #define EAPICALL  call far ptr es:[mpXMSAPI]
  32. #elif (defined ( __SMALL__ ) || defined (__COMPACT__)) // Borland, small
  33.   #define APICALL   call dword ptr mpXMSAPI
  34.   #define EAPICALL  call dword ptr es:mpXMSAPI
  35. #elif (defined ( __LARGE__ ) || defined ( M_I86LM ))  // Large
  36.   #define APICALL   call [mpXMSAPI]
  37.   #define EAPICALL  call es:[mpXMSAPI]
  38. #else
  39.   #error Unsupported compiler or memory model.
  40. #endif
  41.  
  42. #define H_640K  0   // XMS move handle for 640K conventional memory
  43.  
  44. typedef struct tagXMSMOVE   // Extended Memory Move Structure
  45.   {                         // ------------------------------
  46.   DWORD ulLength;           // Number of bytes to transfer
  47.   WORD  uSrcHdl;            // Handle to source block
  48.   DWORD ulSrcOfs;           // Offset into source block
  49.   WORD  uDestHdl;           // Handle to destination block
  50.   DWORD ulDestOfs;          // Offset into destination block
  51.   }
  52. XMSMOVE_T;                  // Handle(s) == H_640K for conventional memory
  53.  
  54. typedef struct tagXMSCTRL   // Extended Memory Control structure
  55.   {                         // ---------------------------------
  56.   WORD    uHandle;          // Handle to the actual XMS block
  57.   WORD    uHiWater;         // `High water' mark: offset of 1st free byte
  58.   WORD    uNumK;            // Number of Kbytes in the block (1..64)
  59.   BOOLEAN fCompacted;       // TRUE if the free blocks have been compacted
  60.   }
  61. XMSCTRL_T;
  62.  
  63. typedef struct tagXMSBLOCKHEADER // Extended Memory Allocation Block Header
  64.   {                              // ---------------------------------------
  65.   WORD uLength;                  // Number of data bytes in the block
  66.   WORD uData;                    // Data, or number of free data bytes, if
  67.   }                              // <uLength> is 0
  68. XMSBLOCKHEADER_T;
  69.  
  70. /* ------------------- Function prototypes -------------------- */
  71.  
  72. /* ------------------------- Globals -------------------------- */
  73. static const char mszIdent[] = { "@(#)XMSLIB v1.00" _CPYR_ };
  74.  
  75. static DWORD mpXMSAPI;              // XMS API entry point
  76. static WORD muXMSerrCode;           // Error code returned by API call
  77.  
  78. static WORD muCtrlBlks    = 0,      // Number of XMS control blocks
  79.             muCurrCtrlBlk = 0;      // Current XMS control block
  80. static XMSCTRL_T * mpXMCtrl = NULL; // Pointer to (array of) ctrl blocks
  81.  
  82. /* ------------------------------------------------------------
  83.  
  84.    The control structure maintained in conventional RAM looks like this:
  85.  
  86.  
  87.    ┌───────────────────┐
  88.  Hi│  Control block N  │   Each control block contains a handle to an
  89.    ├───────────────────┤   XMS memory block (16-bit word) and the current
  90.           .  .  .          `high water' mark in the control block, that
  91.    ├───────────────────┤   is, the offset of the first byte where
  92.    │  Control block 1  │   allocated data can be placed.
  93.    ├───────────────────┤
  94.  Lo│  Control block 0  │   The number of blocks is fixed by XMSopen()
  95.    └───────────────────┘
  96.  
  97.  
  98.    The structure of a partially-used block in XMS memory looks like this:
  99.  
  100.    ┌─┬────┬─┬────────┬─┬──┬─┬───────┬─┬────────────┬─┬────────────────────┐
  101.    │a│XXXX│b│XXXXXXXX│0│cX│d│XXXXXXX│e│XXXXXXXXXXXX│0│00000000000000000000│
  102.    └─┴────┴─┴────────┴─┴──┴─┴───────┴─┴────────────┴─┴────────────────────┘
  103.  
  104.    Here, letters `a',`b' etc. represent the start of allocation units. Each
  105.    contains a 16-bit word holding the LENGTH OF DATA FOLLOWING THE WORD. The
  106.    total length of the unit is therefore (length)+2 bytes. If a length is
  107.    marked as 0, then the block is free and can be (re)used for storage.
  108.  
  109.    If it has never been used, then the first word of the data will also be 0,
  110.    and all space up to the end of the XMS block will thterefore be available.
  111.  
  112.    If the block has been previously used and then freed, the length of the
  113.    (old) block will be stored in the first word of the data. A check for a
  114.    fit of the newly-allocated block can then be made (which may generate a
  115.    new, smaller freed block if the fit is not exact).
  116.  
  117.    ------------------------------------------------------------ */
  118.  
  119. // X M S g e t A P I a d d r e s s
  120. // -------------------------------
  121. // Locates the call gate to the XMS application programming interface
  122. // and stores it in the global <mpXMSAPI>
  123. //
  124. static void _near XMSgetAPIaddress (void)
  125.   {
  126.   _asm {
  127.     mov ax,4310h
  128.     int 2Fh
  129.     mov word ptr [mpXMSAPI],bx
  130.     mov word ptr [mpXMSAPI + 2],es
  131.     }
  132.   }
  133.  
  134. // X M S g e t F r e e M e m
  135. // -------------------------
  136. //
  137. static WORD _near XMSgetFreeMem (WORD * puLargestBlk)
  138.   {
  139.   WORD uTotal, uLargest;
  140.  
  141.   _asm {
  142.     mov   ah,8
  143.     APICALL
  144.     mov   [muXMSerrCode],bx
  145.     mov   uLargest,ax
  146.     mov   uTotal,dx
  147.     }
  148.   *puLargestBlk = uLargest;
  149.   return uTotal;
  150.   }
  151.  
  152. // X M S a l l o c B l o c k
  153. // -------------------------
  154. // Allocate a block of <uKbytes> K bytes from the XMS memory
  155. //
  156. // Returns:
  157. //  Handle to the block, or 0 if none could be allocated
  158. //
  159. static WORD _near XMSallocBlock (WORD uKbytes)
  160.   {
  161.   WORD hXM = 0; // The handle
  162.  
  163.   _asm {
  164.     mov   ah,9
  165.     mov   dx,uKbytes
  166.     APICALL
  167.     mov   [muXMSerrCode],bx
  168.     or    ax,ax
  169.     jz    AllocOver
  170.     mov   hXM,dx
  171.     }
  172.  
  173. AllocOver:
  174.   return hXM;
  175.   }
  176.  
  177. // X M S f r e e B l o c k
  178. // -----------------------
  179. // Free a block of XMS memory accessed through handle <hXM>
  180. //
  181. // Returns:
  182. //  1 if successfully freed, 0 if not
  183. //
  184. static int _near XMSfreeBlock (WORD hXM)
  185.   {
  186.   int fSuccess;
  187.  
  188.   _asm {
  189.     mov   ah,0Ah
  190.     mov   dx,hXM
  191.     APICALL
  192.     mov   [muXMSerrCode],bx
  193.     mov   fSuccess,ax
  194.     }
  195.  
  196.   return fSuccess;
  197.   }
  198.  
  199. // X M S m o v e
  200. // -------------
  201. // Transfer data to or from XMS memory
  202. //
  203. // Argument:
  204. //  <pXMSmv>  Pointer to XMS move structure (see XMS spec)
  205. //
  206. // Returns:
  207. //  1 for success, 0 on error
  208. //
  209. static int _near XMSmove (XMSMOVE_T _far * pXMSmv)
  210.   {
  211.   int fSuccess;
  212.   unsigned uSegment = FP_SEG(pXMSmv),
  213.            uOffset  = FP_OFF(pXMSmv);
  214.   _asm {
  215.     mov   ax,ds
  216.     mov   es,ax
  217.     push  ds
  218.     push  si
  219.     mov   si,uOffset
  220.     mov   ax,uSegment
  221.     mov   ds,ax
  222.     mov   ah,0Bh
  223.     EAPICALL
  224.     mov   es:[muXMSerrCode],bx
  225.     pop   si
  226.     pop   ds
  227.     mov   fSuccess,ax
  228.     }
  229.   return fSuccess;
  230.   }
  231.  
  232. // X M S a l l o c W r i t e
  233. // -------------------------
  234. // Write control information into XMS memory to mark an allocated block
  235. //
  236. // Arguments:
  237. //  <hXM>     Native (XMS-derived) handle to XMS memory block
  238. //  <uOfs>    Offset within the XMS block
  239. //  <uBytes>  Number of data bytes which the block will hold
  240. //
  241. // Returns:
  242. //  Handle to memory, or XMSHNULL on error
  243. //
  244. static XMSHANDLE _near XMSallocWrite (WORD hXM, WORD uOfs, WORD uBytes)
  245.   {
  246.   XMSBLOCKHEADER_T XMShdr;
  247.   XMSMOVE_T XMSmv;
  248.   XMSHANDLE xhXM = XMSHNULL;
  249.  
  250.   XMShdr.uLength  = uBytes;
  251.   XMShdr.uData    = 0;
  252.  
  253.   XMSmv.ulLength  = sizeof(XMSBLOCKHEADER_T);
  254.   XMSmv.uSrcHdl   = H_640K;
  255.   XMSmv.ulSrcOfs  = (DWORD)((char _far *)&XMShdr);
  256.   XMSmv.uDestHdl  = hXM;
  257.   XMSmv.ulDestOfs = (DWORD)uOfs;
  258.  
  259.   if (XMSmove(&XMSmv))
  260.     {
  261.     xhXM = ((DWORD)hXM << 16);
  262.     xhXM += uOfs;
  263.     }
  264.   return xhXM;
  265.   }
  266.  
  267. // C o a l e s c e F r e e U n i t s
  268. // ---------------------------------
  269. // Join contiguous freed blocks in an allocation unit into a single free block
  270. //
  271. // Argument:
  272. //  <pXMCtrl> Pointer to memory control block
  273. //
  274. static void _near _fastcall CoalesceFreeUnits (XMSCTRL_T * pXMCtrl)
  275.   {
  276.   if (!pXMCtrl->fCompacted)
  277.     {
  278.     XMSBLOCKHEADER_T XMShdrThis,
  279.                      XMShdrNext;
  280.     XMSMOVE_T XMSmv;
  281.     WORD uOfs = 0,
  282.          uRefOfs;
  283.  
  284. #if (defined ( _M_DEBUG ) && defined ( _M_VERBOSE ))
  285.   printf("\n--> COALESCING BLOCK WITH HANDLE %04x\n", pXMCtrl->uHandle);
  286. #endif
  287.  
  288.     XMSmv.ulLength  = sizeof(XMSBLOCKHEADER_T);
  289.  
  290.     while (uOfs < pXMCtrl->uHiWater)
  291.       {
  292.       WORD uFreeLen = 0;
  293.       BOOLEAN fBlockEnd = FALSE;
  294.  
  295.       XMSmv.uSrcHdl   = pXMCtrl->uHandle;
  296.       XMSmv.uDestHdl  = H_640K;
  297.       XMSmv.ulSrcOfs  = uRefOfs = uOfs;
  298.       XMSmv.ulDestOfs = (DWORD)((char _far *)&XMShdrThis);
  299.  
  300.       if (!XMSmove(&XMSmv))
  301.         return;
  302.  
  303.       XMSmv.ulDestOfs = (DWORD)((char _far *)&XMShdrNext);
  304.  
  305.       if (XMShdrThis.uLength == 0)
  306.         {
  307.         WORD uExtraLen = 0;
  308.  
  309.         // This block is free: iterate through the following blocks,
  310.         // accumulating the total free length...
  311.         do
  312.           {
  313.           if (XMShdrThis.uData == 0)  // Hit end of chain
  314.             {
  315.             uFreeLen = 0;             // Original now marks end of chain
  316.             fBlockEnd = TRUE;
  317.             break;
  318.             }
  319.  
  320.           uFreeLen += (XMShdrThis.uData + uExtraLen);
  321.           uOfs     += (XMShdrThis.uData + sizeof(WORD));
  322.  
  323.           if (uOfs >= pXMCtrl->uHiWater)
  324.             {
  325.             fBlockEnd = TRUE;
  326.             break;
  327.             }
  328.  
  329.           XMSmv.ulSrcOfs = uOfs;
  330.  
  331.           if (!XMSmove(&XMSmv))       // Read next header
  332.             return;
  333.  
  334.           XMShdrThis = XMShdrNext;
  335.  
  336.           uExtraLen = sizeof(WORD);   // Count 2 byte length on all but 1st block
  337.           }
  338.         while (XMShdrThis.uLength == 0);
  339.  
  340.         // Now update the *original* free block with the coalesced length
  341.         XMShdrNext.uLength = 0;
  342.         if (fBlockEnd == TRUE && uRefOfs == 0)
  343.           // Special case -- all blocks empty
  344.           XMShdrNext.uData = 0;
  345.         else
  346.           XMShdrNext.uData = uFreeLen;
  347.         XMSmv.uSrcHdl   = H_640K;
  348.         XMSmv.ulSrcOfs  = (DWORD)((char _far *)&XMShdrNext);
  349.         XMSmv.uDestHdl  = pXMCtrl->uHandle;
  350.         XMSmv.ulDestOfs = uRefOfs;
  351.  
  352.         XMSmove(&XMSmv);
  353.  
  354.         if (fBlockEnd == TRUE)
  355.           {
  356.           // Update high water mark to new end of block
  357.           pXMCtrl->uHiWater = uRefOfs;
  358.           break;
  359.           }
  360.         }
  361.       else
  362.         uOfs += (XMShdrThis.uLength + sizeof(WORD));
  363.       }
  364.     pXMCtrl->fCompacted = TRUE;
  365.     }
  366.   }
  367.  
  368. // X M S a l l o c P l a n B
  369. // -------------------------
  370. // Allocator called when we've hit the high water mark in the last XMS block
  371. //
  372. // Argument:
  373. //  <uBytes>  Number of data bytes for which storage is required
  374. //
  375. // Returns:
  376. //  Handle to memory, or XMSHNULL on error
  377. //
  378. static XMSHANDLE _near XMSallocPlanB (WORD uBytes)
  379.   {
  380.   XMSCTRL_T * pXMCtrl = mpXMCtrl;
  381.  
  382.   for (muCurrCtrlBlk = 0;
  383.        muCurrCtrlBlk < muCtrlBlks;    // For each XMS block...
  384.        muCurrCtrlBlk++, pXMCtrl++)
  385.     {
  386.     WORD uOfs = 0;    // Start at the bottom...
  387.     XMSBLOCKHEADER_T XMShdr;
  388.     XMSMOVE_T XMSmv;
  389.  
  390.     CoalesceFreeUnits(pXMCtrl);
  391.  
  392.     XMSmv.ulLength  = sizeof(XMSBLOCKHEADER_T);
  393.     XMSmv.uSrcHdl   = pXMCtrl->uHandle;
  394.     XMSmv.uDestHdl  = H_640K;
  395.     XMSmv.ulDestOfs = (DWORD)((char _far *)&XMShdr);
  396.  
  397.     do
  398.       {
  399.       XMSmv.ulSrcOfs  = uOfs;
  400.       XMSmove(&XMSmv);
  401.  
  402.       if (XMShdr.uLength == 0)
  403.         {
  404.         // This block is reusable
  405.         if (XMShdr.uData >= uBytes + sizeof(WORD) + sizeof(WORD))
  406.           {
  407. #if (defined ( _M_DEBUG ) && defined ( _M_VERBOSE ))
  408.           printf("Reusing block of %04x bytes at %04x : New blocks %04x and %04x\n",
  409.                   XMShdr.uData, uOfs, uBytes, XMShdr.uData - uBytes - sizeof(WORD));
  410. #endif
  411.           // Got space in middle of block -- write a new free block beyond it
  412.           XMShdr.uData   -= (uBytes + sizeof(WORD));
  413.           XMSmv.uDestHdl  = XMSmv.uSrcHdl;
  414.           XMSmv.uSrcHdl   = H_640K;
  415.           XMSmv.ulSrcOfs  = XMSmv.ulDestOfs;
  416.           XMSmv.ulDestOfs = uOfs + uBytes + sizeof(WORD);
  417.           XMSmove(&XMSmv);
  418.  
  419.           return XMSallocWrite(pXMCtrl->uHandle, uOfs, uBytes);
  420.           }
  421.         else
  422.           {
  423.           // Not big enough for allocation
  424.           if (XMShdr.uData == 0)
  425.             {
  426.             // Block has never been used; work out length
  427.             WORD uLen = 0xFFFF - uOfs;
  428.  
  429.             if (uLen < uBytes + sizeof(WORD) - 1)
  430.               // No space in this block; try next
  431.               break;
  432.  
  433.             // Got space at end of block
  434.             pXMCtrl->uHiWater = uOfs + uBytes + sizeof(WORD);
  435.             return XMSallocWrite(XMSmv.uSrcHdl, uOfs, uBytes);
  436.             }
  437.  
  438.           // Skip to next
  439.           uOfs += XMShdr.uData;
  440.           }
  441.         }
  442.       else
  443.         // This block ain't reusable: skip to next
  444.         uOfs += XMShdr.uLength;
  445.  
  446.       uOfs += sizeof(WORD);
  447.       }
  448.     while (uOfs < pXMCtrl->uHiWater);
  449.     }
  450.   muCurrCtrlBlk = 0;
  451.   return XMSHNULL;
  452.   }
  453.  
  454. // -----------------------------------------------------------------------
  455. // -----   E x t e r n a l l y - v i s i b l e   f u n c t i o n s   -----
  456. // -----------------------------------------------------------------------
  457.  
  458. // X M S i n s t a l l e d
  459. // -----------------------
  460. // Returns:
  461. //  1 if XMS memory is installed, else 0
  462. //
  463. int XMSinstalled (void)
  464.   {
  465.   int fInstalled;
  466.  
  467.   _asm {
  468.     mov ax,4300h
  469.     int 2Fh
  470.     and ax,0080h
  471.     mov cl,7
  472.     shr al,cl
  473.     mov fInstalled,ax
  474.     }
  475.  
  476.   return fInstalled;
  477.   }
  478.  
  479. // X M S g e t V e r s i o n
  480. // -------------------------
  481. // Gets the version of XMS memory driver (e.g. HIMEM.SYS) in use
  482. //
  483. // Returns:
  484. //  BCD coded number (e.g. v3.21 -> 0x0321), or 0 if not installed or
  485. //  the XMS library's XMSopen() function has not been called.
  486. //
  487. WORD XMSgetVersion (void)
  488.   {
  489.   WORD uVersion = 0;
  490.  
  491.   if (XMSinstalled() && mpXMSAPI != 0L)
  492.     {
  493.     _asm {
  494.       mov   ah,0
  495.       APICALL
  496.       mov   uVersion,bx
  497.       }
  498.     }
  499.   return uVersion;
  500.   }
  501.  
  502. // X M S o p e n
  503. // -------------
  504. // `Open communications' with extended memory
  505. //
  506. // Argument:
  507. //  <uKbytes> Number of Kbytes of extended memory to use.
  508. //
  509. // Returns:
  510. //  1 for success
  511. //  0 for failure
  512. //
  513. // Notes:
  514. //  A value of 0 for <uKbytes> will attempt to use *all* available XMS memory.
  515. //  The call will fail if the entire amount requested can not be obtained.
  516. //  At least 64K extended memory must be available as a contiguous block.
  517. //
  518. int XMSopen (WORD uKbytes)
  519.   {
  520.   if (muCtrlBlks == 0)
  521.     {
  522.     WORD uKlargestAvail,
  523.          uLastK;
  524.  
  525.     XMSgetAPIaddress();
  526.     XMSgetFreeMem(&uKlargestAvail);
  527.  
  528.     if (uKbytes == 0)
  529.       {
  530.       if (uKlargestAvail >= 64)
  531.         muCtrlBlks = (uKlargestAvail / 64);
  532.       }
  533.     else if (uKlargestAvail >= uKbytes)
  534.       muCtrlBlks = (uKbytes / 64) + 1;
  535.  
  536.     if ((uLastK = (uKbytes % 64)) == 0)
  537.       uLastK = 64;
  538.  
  539.     if (muCtrlBlks > 0)
  540.       {
  541.       muCurrCtrlBlk = 0;
  542.  
  543.       if ((mpXMCtrl = (XMSCTRL_T *)calloc(muCtrlBlks, sizeof(XMSCTRL_T)))
  544.            != NULL)
  545.         {
  546.         // XMS control blocks are pre-filled with zeroes. Now do the
  547.         // actual carving up of the extended memory...
  548.  
  549.         XMSCTRL_T * pXMCtrl = mpXMCtrl;
  550.         WORD uCount;
  551.  
  552.         for (uCount = 0; uCount < muCtrlBlks; uCount++)
  553.           {
  554.           WORD uNumK   = (uCount == muCtrlBlks - 1 ? uLastK : 64);
  555.           WORD uHandle = XMSallocBlock(uNumK);
  556.  
  557.           if (uHandle > 0)
  558.             {
  559.             pXMCtrl->uHandle    = uHandle;
  560.             pXMCtrl->uNumK      = uNumK;
  561.             pXMCtrl->fCompacted = TRUE;
  562.             pXMCtrl++;
  563.             }
  564.           else
  565.             {
  566.             muCtrlBlks = uCount;
  567.             break;
  568.             }
  569.           }
  570.         }
  571.       else
  572.         muCtrlBlks = 0;
  573.       }
  574.  
  575.     return(muCtrlBlks > 0);
  576.     }
  577.  
  578.   // XMSopen() already called with no intervening XMSclose()
  579.   return 0;
  580.   }
  581.  
  582. // X M S c l o s e
  583. // ---------------
  584. // `Close communications' with XMS memory
  585. //
  586. // Returns:
  587. //  1 for success
  588. //  0 for failure
  589. //
  590. // Notes:
  591. //  De-allocates *all* memory associated with XMSalloc()ed data
  592. //
  593. int XMSclose (void)
  594.   {
  595.   if (muCtrlBlks > 0)
  596.     {
  597.     WORD uCount = muCtrlBlks,
  598.                    uFreed = 0;
  599.     XMSCTRL_T * pXMCtrl   = mpXMCtrl + uCount;
  600.  
  601.     do
  602.       {
  603.       if (XMSfreeBlock(pXMCtrl->uHandle))
  604.         uFreed++;
  605.       pXMCtrl--;
  606.       }
  607.     while (uCount-- > 0);
  608.  
  609.     if (uFreed == muCtrlBlks)
  610.       {
  611.       free(mpXMCtrl);
  612.       muCtrlBlks    = 0;
  613.       muCurrCtrlBlk = 0;
  614.       mpXMCtrl      = NULL;
  615.       return 1;
  616.       }
  617.     }
  618.   return 0;
  619.   }
  620.  
  621. // X M S a l l o c
  622. // ---------------
  623. // Allocate space for data in extended memory
  624. //
  625. // Argument:
  626. //  <uBytes>  Number of data bytes for which storage is required
  627. //
  628. // Returns:
  629. //  Handle to memory, or XMSHNULL on error
  630. //
  631. // Notes:
  632. //  Overhead is one (WORD) in XMS memory per entry
  633. //
  634. XMSHANDLE XMSalloc (WORD uBytes)
  635.   {
  636.   XMSCTRL_T * pXMCtrl  = mpXMCtrl + muCurrCtrlBlk;
  637.   XMSHANDLE   xhXM     = XMSHNULL;
  638.   unsigned    uHiWater = pXMCtrl->uHiWater,
  639.               uNumK    = pXMCtrl->uNumK;
  640.  
  641.   // Word-align
  642.  
  643.   if (uBytes % 2)
  644.     uBytes++;
  645.  
  646.   // Check for wrap at 64K boundary
  647.  
  648.   while ((uBytes + uHiWater + sizeof(WORD) < uBytes) ||
  649.          ((uNumK < 64) &&
  650.          (uBytes + uHiWater + sizeof(WORD) > uNumK * 1024)))
  651.     {
  652.     if (++muCurrCtrlBlk == muCtrlBlks)
  653.       return XMSallocPlanB(uBytes);
  654.  
  655.     pXMCtrl++;
  656.     uHiWater = pXMCtrl->uHiWater;
  657.     uNumK    = pXMCtrl->uNumK;
  658.     }
  659.  
  660.   xhXM = XMSallocWrite(pXMCtrl->uHandle, uHiWater, uBytes);
  661.  
  662.   if (xhXM != XMSHNULL)
  663.     pXMCtrl->uHiWater = uHiWater + uBytes + sizeof(WORD);
  664.   else
  665.     muCurrCtrlBlk = 0;
  666.  
  667.   return xhXM;
  668.   }
  669.  
  670. // X M S f r e e
  671. // -------------
  672. // Free an allocation unit obtained with XMSalloc()
  673. //
  674. // Arguments:
  675. //  <xhXM>    Handle to XMS memory
  676. //
  677. // Returns:
  678. //  1 on success, 0 on failure
  679. //
  680. int XMSfree (XMSHANDLE xhXM)
  681.   {
  682.   XMSBLOCKHEADER_T XMShdr;
  683.   XMSMOVE_T XMSmv;
  684.  
  685.   XMSmv.ulLength  = sizeof(XMSBLOCKHEADER_T);
  686.   XMSmv.uSrcHdl   = (WORD)(xhXM >> 16);
  687.   XMSmv.ulSrcOfs  = (WORD)xhXM; // Get the length, too
  688.   XMSmv.uDestHdl  = H_640K;
  689.   XMSmv.ulDestOfs = (DWORD)((char _far *)&XMShdr);
  690.  
  691.   if (XMSmove(&XMSmv))
  692.     {
  693.     XMShdr.uData   = XMShdr.uLength;  // Old len stored in 1st two data bytes
  694.     XMShdr.uLength = 0;               // Length now marked as zero = reusable
  695.  
  696.     XMSmv.uDestHdl  = XMSmv.uSrcHdl;
  697.     XMSmv.ulDestOfs = XMSmv.ulSrcOfs; // Write over length bytes
  698.     XMSmv.uSrcHdl   = H_640K;
  699.     XMSmv.ulSrcOfs  = (DWORD)((char _far *)&XMShdr);
  700.  
  701.     if (XMSmove(&XMSmv))
  702.       {
  703.       WORD uBlock;
  704.       XMSCTRL_T * pXMCtrl = mpXMCtrl;
  705.  
  706.       for (uBlock = 0; uBlock < muCtrlBlks; uBlock++)
  707.         {
  708.         if (pXMCtrl->uHandle == XMSmv.uDestHdl)
  709.           {
  710.           pXMCtrl->fCompacted = FALSE;
  711.           break;
  712.           }
  713.         pXMCtrl++;
  714.         }
  715.       return 1;
  716.       }
  717.     }
  718.   return 0;
  719.   }
  720.  
  721. // X M S e r r o r C o d e
  722. // -----------------------
  723. // Get the error code associated with the most recent XMS API call
  724. //
  725. WORD XMSerrorCode (void)
  726.   {
  727.   return muXMSerrCode;
  728.   }
  729.  
  730. // X M S g e t L e n
  731. // -----------------
  732. // Get the length of the space associated with an XMS handle
  733. //
  734. // Arguments:
  735. //  <xhXM>    Handle to XMS memory
  736. //
  737. // Returns:
  738. //  Length of data, or 0 on error
  739. //
  740. WORD XMSgetLen (XMSHANDLE xhXM)
  741.   {
  742.   WORD hXM    = (WORD)(xhXM >> 16),
  743.        uOfs   = (WORD)xhXM,
  744.        uBlock = 0,
  745.        uLen   = 0;
  746.   XMSCTRL_T * pXMCtrl = mpXMCtrl;
  747.  
  748.   while (uBlock < muCtrlBlks && pXMCtrl->uHandle != hXM)
  749.     {
  750.     uBlock++;
  751.     pXMCtrl++;
  752.     }
  753.  
  754.   if (uBlock < muCtrlBlks)
  755.     {
  756.     XMSMOVE_T XMSmv;
  757.  
  758.     XMSmv.ulLength  = sizeof(WORD);
  759.     XMSmv.uSrcHdl   = hXM;
  760.     XMSmv.ulSrcOfs  = uOfs;
  761.     XMSmv.uDestHdl  = H_640K;
  762.     XMSmv.ulDestOfs = (DWORD)((char _far *)&uLen);
  763.  
  764.     XMSmove(&XMSmv);
  765.     }
  766.   return uLen;
  767.   }
  768.  
  769. // X M S g e t E x t
  770. // -----------------
  771. // Get data from XMS memory to conventional memory; extended version
  772. //
  773. // Arguments:
  774. //  <pDest>   Pointer to source buffer
  775. //  <xhXM>    Handle to XMS memory
  776. //  <uSrcOfs> Offset of data in the stored block (0 = start)
  777. //  <uBytes>  Number of bytes to copy
  778. //
  779. // Returns:
  780. //  1 on success, 0 on failure
  781. //
  782. int XMSgetExt (void * pDest, XMSHANDLE xhXM, WORD uSrcOfs, WORD uBytes)
  783.   {
  784.   XMSMOVE_T XMSmv;
  785.  
  786.   XMSmv.ulLength  = uBytes;
  787.   XMSmv.uSrcHdl   = (WORD)(xhXM >> 16);
  788.   XMSmv.ulSrcOfs  = (WORD)xhXM + sizeof(WORD) + uSrcOfs;
  789.   XMSmv.uDestHdl  = H_640K;
  790.   XMSmv.ulDestOfs = (DWORD)((char _far *)pDest);
  791.  
  792.   return XMSmove(&XMSmv);
  793.   }
  794.  
  795. // X M S g e t
  796. // -----------
  797. // Get data from XMS memory to conventional memory
  798. //
  799. // Arguments:
  800. //  <pDest>   Pointer to source buffer
  801. //  <xhXM>    Handle to XMS memory
  802. //
  803. // Returns:
  804. //  1 on success, 0 on failure
  805. //
  806. // Notes:
  807. //  Automatically sets number of bytes copied out to the number copied in.
  808. //  Caller *must* ensure that buffer at <pDest> is big enough.
  809. //
  810. int XMSget (void * pDest, XMSHANDLE xhXM)
  811.   {
  812.   XMSMOVE_T XMSmv;
  813.  
  814.   XMSmv.ulLength  = XMSgetLen(xhXM);
  815.   XMSmv.uSrcHdl   = (WORD)(xhXM >> 16);
  816.   XMSmv.ulSrcOfs  = (WORD)xhXM + sizeof(WORD);
  817.   XMSmv.uDestHdl  = H_640K;
  818.   XMSmv.ulDestOfs = (DWORD)((char _far *)pDest);
  819.  
  820.   return XMSmove(&XMSmv);
  821.   }
  822.  
  823. // X M S p u t
  824. // -----------
  825. // Put data in XMS memory from conventional memory
  826. //
  827. // Arguments:
  828. //  <xhXM>    Handle to XMS memory
  829. //  <pSrc>    Pointer to source buffer
  830. //  <uBytes>  Bytes to copy
  831. //
  832. // Returns:
  833. //  1 on success, 0 on failure
  834. //
  835. int XMSput (XMSHANDLE xhXM, const void * pSrc, WORD uBytes)
  836.   {
  837.   XMSMOVE_T XMSmv;
  838.  
  839.   if (uBytes == 0)
  840.     uBytes = XMSgetLen(xhXM);
  841.   else if (uBytes % 2)
  842.     uBytes++;
  843.  
  844.   XMSmv.ulLength  = uBytes;
  845.   XMSmv.uSrcHdl   = H_640K;
  846.   XMSmv.ulSrcOfs  = (DWORD)((char _far *)pSrc);
  847.   XMSmv.uDestHdl  = (WORD)(xhXM >> 16);
  848.   XMSmv.ulDestOfs = (WORD)xhXM + sizeof(WORD);
  849.  
  850.   return XMSmove(&XMSmv);
  851.   }
  852.  
  853. #if defined ( _M_DEBUG)
  854.  
  855. // -----------------------------------------------------------------------
  856. // -------------   D e b u g g i n g   f u n c t i o n s   ---------------
  857. // -----------------------------------------------------------------------
  858.  
  859. // X M S d u m p C o n t r o l B l o c k
  860. // -------------------------------------
  861. static void _near XMSdumpControlBlock (const XMSCTRL_T * pXMCtrl)
  862.   {
  863.   printf("%04x  %04x (%5u)  %04x (%5u)\n", pXMCtrl->uHandle, pXMCtrl->uHiWater,
  864.          pXMCtrl->uHiWater, pXMCtrl->uNumK, pXMCtrl->uNumK);
  865.   }
  866.  
  867. // X M S d u m p E x t e n d e d M e m o r y B l o c k
  868. // ---------------------------------------------------
  869. static void _near XMSdumpExtendedMemoryBlock (const XMSCTRL_T * pXMCtrl)
  870.   {
  871.   XMSMOVE_T XMSmv;
  872.   WORD uOfs = 0;
  873.   union
  874.     {
  875.     XMSBLOCKHEADER_T XMShdr;
  876.     unsigned char    bBuffer[sizeof(WORD) + 16];
  877.     }
  878.   Data;
  879.  
  880.   if (pXMCtrl->uHiWater == 0) // Has never been used
  881.     return;
  882.  
  883.   XMSmv.ulLength  = sizeof(Data);
  884.   XMSmv.uSrcHdl   = pXMCtrl->uHandle;
  885.   XMSmv.uDestHdl  = H_640K;
  886.   XMSmv.ulDestOfs = (DWORD)((char _far *)&Data);
  887.  
  888.   do
  889.     {
  890.     WORD uRemaining = pXMCtrl->uHiWater - uOfs;
  891.  
  892.     if (uRemaining < sizeof(Data))
  893.       XMSmv.ulLength = uRemaining;
  894.  
  895.     XMSmv.ulSrcOfs = uOfs;
  896.  
  897.     if (XMSmove(&XMSmv))
  898.       {
  899.       WORD i,
  900.            iMax = Data.XMShdr.uLength;
  901.  
  902.       if (iMax == 0)
  903.         iMax = Data.XMShdr.uData;
  904.  
  905.       if (iMax > 16)
  906.         iMax = 16;
  907.  
  908.       printf("%04x: %04x  ", uOfs, Data.XMShdr.uLength);
  909.       for (i = 0; i < 16; i++)
  910.         if (i < iMax)
  911.           printf("%02x ", Data.bBuffer[i + sizeof(WORD)]);
  912.         else
  913.           printf("   ");
  914.       printf("  ");
  915.       for (i = 0; i < iMax; i++)
  916.         {
  917.         unsigned char c = Data.bBuffer[i + sizeof(WORD)];
  918.         printf("%c", iscntrl(c) ? '.' : c);
  919.         }
  920.       printf("\n");
  921.       }
  922.     else
  923.       {
  924.       printf("ERROR %04x\n", XMSerrorCode());
  925.       break;
  926.       }
  927.  
  928.     if (Data.XMShdr.uLength == 0)
  929.       uOfs += Data.XMShdr.uData;
  930.     else
  931.       uOfs += Data.XMShdr.uLength;
  932.  
  933.     uOfs += sizeof(WORD);
  934.     }
  935.   while (uOfs < pXMCtrl->uHiWater &&
  936.          (Data.XMShdr.uLength > 0 || Data.XMShdr.uData > 0));
  937.   }
  938.  
  939. // X M S d u m p
  940. // -------------
  941. void XMSdump (WORD uDumpFlags)
  942.   {
  943.   XMSCTRL_T * pXMCtrl;
  944.   WORD uCtrlBlk;
  945.  
  946.   printf("\n*** XMS Dump ***\n\n");
  947.  
  948.   if (uDumpFlags & XMSDUMP_CTRL)
  949.     {
  950.     printf("Control blocks:\n\n");
  951.     printf("  Num:  Hdl  MaxH  (MaxD)  NoKH  (NoKD)\n");
  952.  
  953.     for (uCtrlBlk = 0, pXMCtrl = mpXMCtrl;
  954.          uCtrlBlk < muCtrlBlks;
  955.          uCtrlBlk++)
  956.       {
  957.       printf("%5u: ", uCtrlBlk);
  958.       XMSdumpControlBlock(pXMCtrl++);
  959.       }
  960.     }
  961.  
  962.   if (uDumpFlags & XMSDUMP_DATA)
  963.     {
  964.     printf("\nExtended memory:");
  965.  
  966.     for (uCtrlBlk = 0, pXMCtrl = mpXMCtrl;
  967.          uCtrlBlk < muCtrlBlks;
  968.          uCtrlBlk++)
  969.       {
  970.       printf("\n\nBlock %5u:\n\n", uCtrlBlk);
  971.       XMSdumpExtendedMemoryBlock(pXMCtrl++);
  972.       }
  973.     }
  974.   printf("\n****************\n\n");
  975.   }
  976.  
  977. #endif  // _M_DEBUG
  978.